package se.prb.cheesymock;

import java.util.Iterator;
import java.util.List;

import junit.framework.AssertionFailedError;
import junit.framework.TestCase;

import org.apache.log4j.Logger;

@SuppressWarnings("unused")
public class CheesyMockTest2 extends TestCase {
	Logger log = Logger.getLogger(CheesyMockTest.class);

	public void testSimpleMock() throws Exception {
		TestInterface test = createMock(new Object() {
			public void method1() {
				log.info("Test object got method 1! W00t!");
			}
		});
		test.method1();
		test.method2();
	}

	public void testMockWithNullArgument() throws Exception {
		StatusObject receiver = new StatusObject() {
			public void method1(Object obj) {
				this.status = "japp";
			}
		};
		TestInterface test = createMock(receiver);
		assertNull(receiver.status);
		test.method1(null);
		assertEquals("japp", receiver.status);
	}

	public void testMockWithOverloadedNullArgument() throws Exception {
		StatusObject receiver = new StatusObject() {
			public void method1(String str) {
				this.status = "japp";
			}
		};
		TestInterface test = createMock(receiver);
		assertNull(receiver.status);
		test.method1(null);
		assertEquals("japp", receiver.status);
	}

	public void testMockWithPrimitiveArguments() throws Exception {
		StatusObject receiver = new StatusObject() {
			public void method1(int i) {
				this.status = Integer.valueOf(i);
			}
		};
		TestInterface test = createMock(receiver);
		assertNull(receiver.status);
		test.method1(23);
		assertTrue(receiver.status instanceof Integer);
		assertEquals(23, ((Integer) receiver.status).intValue());
	}

	public void testMockWithArguments() throws Exception {
		StatusObject receiver = new StatusObject() {
			public void method1(Object obj) {
				this.status = "bu!";
			}

			public void method1(String str) {
				this.status = str;
			}
		};
		TestInterface test = createMock(receiver);
		assertNull(receiver.status);
		test.method1("hej");
		assertEquals("hej", receiver.status);
	}

	public void testMockWithPrimitiveReturnValue() throws Exception {
		TestInterface test = createMock(null);
		assertEquals("method4".hashCode(), test.method4());
		test = createMock(new Object());
		assertEquals("method4".hashCode(), test.method4());
		test = createMock(new Object() {
			public int method4() {
				return 23;
			}
		});
		assertEquals(23, test.method4());
		test = createMock(new Object() {
			public Integer method4() {
				return Integer.valueOf(23);
			}
		});
		assertEquals("method4".hashCode(), test.method4());
	}

	public void testPrimitiveIntReturnValue() throws Exception {
		Object delegate = null;
		PrimitiveTestInterface test = createPrimitiveMock(delegate);
		assertEquals("returnInt".hashCode(), test.returnInt());
		// same value again
		test = createPrimitiveMock(null);
		assertEquals("returnInt".hashCode(), test.returnInt());
	}

	public void testPrimitiveLongReturnValue() throws Exception {
		PrimitiveTestInterface test = createPrimitiveMock(null);
		assertEquals("returnLong".hashCode(), test.returnLong());
	}

	public void testPrimitiveFloatReturnValue() throws Exception {
		PrimitiveTestInterface test = createPrimitiveMock(null);
		assertEquals("returnFloat".hashCode(), test.returnFloat(), 0);
	}

	public void testPrimitiveDoubleReturnValue() throws Exception {
		PrimitiveTestInterface test = createPrimitiveMock(null);
		assertEquals("returnDouble".hashCode(), test.returnDouble(), 0);
	}

	public void testPrimitiveShortReturnValue() throws Exception {
		PrimitiveTestInterface test = createPrimitiveMock(null);
		assertEquals((short) "returnShort".hashCode(), test.returnShort());
	}

	public void testPrimitiveBooleanReturnValue() throws Exception {
		PrimitiveTestInterface test = createPrimitiveMock(null);
		assertEquals(true, test.returnBoolean());
	}

	public void testPrimitiveByteReturnValue() throws Exception {
		PrimitiveTestInterface test = createPrimitiveMock(null);
		assertEquals((byte) "returnByte".hashCode(), test.returnByte());
	}

	public void testPrimitiveCharReturnValue() throws Exception {
		PrimitiveTestInterface test = createPrimitiveMock(null);
		assertEquals('r', test.returnChar());
	}

	public void testPrimitiveIntReturnValue2() throws Exception {
		Object delegate = null;
		PrimitiveTestInterface2 test = createPrimitiveMock2(delegate);
		assertEquals("returnInt".hashCode(), test.returnInt().intValue());
		// same value again
		test = createPrimitiveMock2(null);
		assertEquals("returnInt".hashCode(), test.returnInt().intValue());
	}

	public void testPrimitiveLongReturnValue2() throws Exception {
		PrimitiveTestInterface2 test = createPrimitiveMock2(null);
		assertEquals("returnLong".hashCode(), test.returnLong().intValue());
	}

	public void testPrimitiveFloatReturnValue2() throws Exception {
		PrimitiveTestInterface2 test = createPrimitiveMock2(null);
		assertEquals("returnFloat".hashCode(), test.returnFloat().intValue(), 0);
	}

	public void testPrimitiveDoubleReturnValue2() throws Exception {
		PrimitiveTestInterface2 test = createPrimitiveMock2(null);
		assertEquals("returnDouble".hashCode(), test.returnDouble().intValue(), 0);
	}

	public void testPrimitiveShortReturnValue2() throws Exception {
		PrimitiveTestInterface2 test = createPrimitiveMock2(null);
		assertEquals((short) "returnShort".hashCode(), test.returnShort().intValue());
	}

	public void testPrimitiveBooleanReturnValue2() throws Exception {
		PrimitiveTestInterface2 test = createPrimitiveMock2(null);
		assertEquals(true, test.returnBoolean().booleanValue());
	}

	public void testPrimitiveByteReturnValue2() throws Exception {
		PrimitiveTestInterface2 test = createPrimitiveMock2(null);
		assertEquals((byte) "returnByte".hashCode(), test.returnByte().intValue());
	}

	public void testPrimitiveCharReturnValue2() throws Exception {
		PrimitiveTestInterface2 test = createPrimitiveMock2(null);
		assertEquals('r', test.returnChar().charValue());
	}

	public void testMockWithObjectReturnValue() throws Exception {
		TestInterface test = createMock(null);
		assertEquals("method3", test.method3());
		test = createMock(new Object());
		assertEquals("method3", test.method3());
		test = createMock(new Object() {
			public String method3() {
				return "hej";
			}
		});
		assertEquals("hej", test.method3());
	}

	public void testMockWithException() throws Exception {
		TestInterface test = createMock(new Object() {
			public void method5() {
				throw new IllegalArgumentException("hej");
			}
		});
		try {
			test.method5();
			fail();
		} catch (IllegalArgumentException iae) {
			assertEquals("hej", iae.getMessage());
		}
	}

	public void testWithAssertionInReceiver() throws Exception {
		TestInterface test = createMock(new Object() {
			public void method1() {
				fail();
			}
		});
		boolean caughtIt = true;
		try {
			test.method1();
			caughtIt = false;
		} catch (AssertionFailedError afe) {
		}
		assertTrue(caughtIt);
	}

	public void testWithOverride() throws Exception {
		OverriddenStatusObject receiver = new OverriddenStatusObject() {
			@Override
			public void method1(String str) {
				status = "kalas";
			}
		};
		TestInterface test = createMock(receiver);
		assertNull(receiver.status);
		test.method1("hej");
		assertEquals("kalas", receiver.status);
	}

	@SuppressWarnings("unchecked")
	public void testRealWorldExampleWithList() throws Exception {
		final Object delegate = new Object() {
			int size = 0;

			public void add(int index, Object obj) {
				size++;
			}

			public Iterator iterator() throws Exception {
				return CheesyMock.createMock(Iterator.class, null, CheesyMockDefaultBehaviour.VALUES_FROM_METHOD_NAME);
			}

			public int size() {
				return size;
			}
		};
		List<String> test = (List<String>) createListMock(delegate);
		assertEquals(0, test.size());
		test.add(1, "hej");
		assertEquals(1, test.size());
		Iterator<String> iter = test.iterator();
		assertNotNull(iter);
	}

	public void testReturnStringArray() throws Exception {
		TestInterface test = createMock(new Object() {
			public String[] method6() {
				return new String[] { "hej", "hå" };
			}
		});
		String ret[] = test.method6();
		assertNotNull(ret);
		assertEquals("hej", ret[0]);
		assertEquals("hå", ret[1]);
	}

	public void testReturnNullStringArray() throws Exception {
		TestInterface test = createMock(null);
		String ret[] = test.method6();
		assertNull(ret);
	}

	public void testReturnIntArray() throws Exception {
		TestInterface test = createMock(new Object() {
			public int[] method7() {
				return new int[] { 17, 23 };
			}
		});
		int ret[] = test.method7();
		assertNotNull(ret);
		assertEquals(17, ret[0]);
		assertEquals(23, ret[1]);
	}

	public void testReturnNullIntArray() throws Exception {
		TestInterface test = createMock(null);
		int ret[] = test.method7();
		assertNull(ret);
	}

	public void testIntAccessor() throws Exception {
		final Object delegate = new Object() {
			public int value1 = 23;
		};
		AccessorTestInterface test = createAccessorMock(delegate);
		assertNotNull(test);
		assertEquals(23, test.getValue1());
		test.setValue1(17);
		assertEquals(17, test.getValue1());
	}

	public void testIntAccessorResolutionOrder() throws Exception {
		AccessorTestInterface test = createAccessorMock(new Object() {
			public int value1 = 23;

			public int getValue1() {
				return 17;
			}
		});
		assertNotNull(test);
		assertEquals(17, test.getValue1());
	}

	public void testStringAccessor() throws Exception {
		AccessorTestInterface test = createAccessorMock(new Object() {
			public String value2 = "hej";
		});
		assertNotNull(test);
		assertEquals("hej", test.getValue2());
		test.setValue2("då");
		assertEquals("då", test.getValue2());
	}

	public void testCallWithGenerics() {
		TestInterface test = createMock(new Object() {
			public String method3() {
				return "Hello, world";
			}
		});
		test.method1();
		assertEquals("Hello, world", test.method3());
	}

	public void testEqualsMockWithDelegateObject() {
		final Object delegate = new Object() {
		};
		TestInterface test = createMock(delegate);
		assertEquals(test, test);
	}

	public void testEqualsMockWithoutDelegateObject() {
		TestInterface test = createMock(null);
		assertEquals(test, test);
	}

	private PrimitiveTestInterface createPrimitiveMock(Object delegate) {
		return CheesyMock.createMock(PrimitiveTestInterface.class, delegate,
				CheesyMockDefaultBehaviour.VALUES_FROM_METHOD_NAME);
	}

	private PrimitiveTestInterface2 createPrimitiveMock2(Object delegate) {
		return CheesyMock.createMock(PrimitiveTestInterface2.class, delegate,
				CheesyMockDefaultBehaviour.VALUES_FROM_METHOD_NAME);
	}

	private TestInterface createMock(final Object delegate) {
		return CheesyMock.createMock(TestInterface.class, delegate, CheesyMockDefaultBehaviour.VALUES_FROM_METHOD_NAME);
	}

	private List<?> createListMock(final Object delegate) {
		return CheesyMock.createMock(List.class, delegate, CheesyMockDefaultBehaviour.VALUES_FROM_METHOD_NAME);
	}

	private AccessorTestInterface createAccessorMock(final Object delegate) {
		return CheesyMock.createMock(AccessorTestInterface.class, delegate,
				CheesyMockDefaultBehaviour.VALUES_FROM_METHOD_NAME);
	}

	public static class StatusObject {
		public Object status = null;
	}

	public static class OverriddenStatusObject extends StatusObject {
		public void method1(String str) {
			status = str;
		}
	}

	public static interface TestInterface {
		public void method1();

		public void method1(int i);

		public void method1(Object obj);

		public void method1(String str);

		public void method2();

		public String method3();

		public int method4();

		public void method5() throws IllegalArgumentException;

		public String[] method6();

		public int[] method7();
	}

	public static interface PrimitiveTestInterface {
		public int returnInt();

		public long returnLong();

		public float returnFloat();

		public double returnDouble();

		public short returnShort();

		public boolean returnBoolean();

		public byte returnByte();

		public char returnChar();
	}

	public static interface PrimitiveTestInterface2 {
		public Integer returnInt();

		public Long returnLong();

		public Float returnFloat();

		public Double returnDouble();

		public Short returnShort();

		public Boolean returnBoolean();

		public Byte returnByte();

		public Character returnChar();
	}

	public static interface AccessorTestInterface {
		public int getValue1();

		public void setValue1(int value1);

		public String getValue2();

		public void setValue2(String value2);
	}
}
